﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using Xenta.Api;
using Xenta.Plugins;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Gets the list of registered payment processor.
    /// </summary>
    public class ListPaymentProcessors : ListDependencies<IPaymentProcessor>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the list of registered payment processor.
    /// </summary>
    public class ListIPNHandlers : ListDependencies<IIPNHandler>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the list of registered sales models.
    /// </summary>
    public class ListSalesModel : ListDependencies<ISalesModel>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the list of registered shipping methods.
    /// </summary>
    public class ListShippingMethods : ListDependencies<IShippingMethod>,
        IApiOperation
    {
    }

    #endregion

    #region __Workflow__

    /// <summary>
    /// Gets a collection of available shipping options.
    /// </summary>
    /// <remarks>
    /// The result entry structure:
    ///  - Info(DependencyInfo)
    ///  - Price(Decimal)
    ///  - Availability(Boolean)
    /// </remarks>
    public class GetAvailableShippingOptions : Operation<dynamic>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the customer shipping address identifier.
        /// </summary>
        public int ShippingAddressID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the seller address identifier.
        /// </summary>
        public int SellerAddressID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out dynamic result)
        {
            var shippingAddress = uow.Execute(new GetCustomerAddress
            {
                ThrowsException = true,
                EntityID = ShippingAddressID
            }).Result;
            var sellerAddress = uow.Execute(new GetSellerAddress
            {
                ThrowsException = true,
                EntityID = SellerAddressID
            }).Result;

            var opts = new List<ExpandoObject>();
            using(var ioc = uow.Infrastructure.DependencyResolver())
            {
                foreach(var i in uow.Execute(new ListShippingMethods()).Result)
                {
                    var method = ioc.Resolve(i.ID)
                        as IShippingMethod;
                    dynamic opt = new ExpandoObject();
                    opt.Info = i;
                    opt.Price = Decimal.Zero;
                    opt.Availability = false;

                    if(method.CanShipFrom(sellerAddress) && 
                        method.CanShipTo(shippingAddress))
                    {
                        opt.Price = method.CalcShippingPrice(sellerAddress, shippingAddress);
                        opt.Availability = true;
                    }
                    opts.Add(opt);
                }
            }
            result = opts;
        }

        #endregion
    }

    /// <summary>
    /// Gets a collection of available sales models.
    /// </summary>
    /// <remarks>
    /// The result entry structure:
    ///  - Info(DependencyInfo)
    ///  - Availability(Boolean)
    /// </remarks>
    public class GetAvailableSalesModels : Operation<dynamic>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the shopping cart identifier.
        /// </summary>
        public int CartID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out dynamic result)
        {
            var opts = new List<ExpandoObject>();
            var cart = uow.Execute(new GetShoppingCart
            {
                EntityID = CartID
            }).Result;
            using(var ioc = uow.Infrastructure.DependencyResolver())
            {
                foreach(var i in uow.Execute(new ListSalesModel()).Result)
                {
                    var model = ioc.Resolve(i.ID) 
                        as ISalesModel;
                    dynamic opt = new ExpandoObject();
                    opt.Info = i;
                    opt.Availability = false;
                    if(cart.Items.All(x => model.CanSale(x.SalesItem)))
                        opt.Availability = true;
                    opts.Add(opt);
                }
            }
            result = opts;
        }

        #endregion
    }

    /// <summary>
    /// Gets a collection of available payment methods.
    /// </summary>
    /// <remarks>
    /// The result entry structure:
    ///  - Info(DependencyInfo)
    ///  - HandlingFee(Decimal)
    ///  - Availability(Boolean)
    /// </remarks>
    public class GetAvailablePaymentMethods : Operation<dynamic>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets an invoice indentifier.
        /// </summary>
        public int InvoiceID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the payment amount to calculate handling fee.
        /// </summary>
        public decimal PaymentAmount
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out dynamic result)
        {
            var opts = new List<ExpandoObject>();
            var invoice = uow.Execute(new GetInvoice
            {
                EntityID = InvoiceID
            }).Result;
            if(invoice != null)
            {
                using(var ioc = uow.Infrastructure.DependencyResolver())
                {
                    foreach(var i in uow.Execute(new ListPaymentProcessors()).Result)
                    {
                        var processor = ioc.Resolve(i.ID)
                            as IPaymentProcessor;
                        dynamic opt = new ExpandoObject();
                        opt.Info = i;
                        opt.Availability = false;
                        opt.HandlingFee = Decimal.Zero;

                        if(processor.CanApplyTo(invoice))
                        {
                            opt.Availability = true;
                            opt.HandlingFee = processor.CalcHandlingFee(invoice, PaymentAmount);
                        }
                        opts.Add(opt);
                    }
                }
            }
            result = opts;
        }

        #endregion
    }

    #endregion  
}
